/**
 * @file shmem.c
 * Betriebssysteme Shared Memory File.
 *
 * Beispiel 2
 *
 * @author Markus Scharitzer 1410258040 <markus.scharitzer@technikum-wien.at>
 * @author Helene Wiesinger 1410258004 <helene.wiesinger@technikum-wien.at>
 * @author Siegfried Becher 141025035 <siegfried.becher@technikum-wien.at>
 * @date 2015/06/04
 *
 * @version 1.0
 *
 * Implements shared functions for the shared memory assignment.
 *
 */

/*
 * -------------------------------------------------- includes --
 */
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sem182.h>
#include "shmem.h"
#include <limits.h>


/*
 * --------------------------------------------------- defines --
 */

#define PERMISSIONS 0660
/*
 * -------------------------------------------------- typedefs --
 */

/*
 * --------------------------------------------------- globals --
 */
static int SHARED_MEMORY_ID = -1;
static int SHM_READ = 0;
static int SHM_WRITE = 0;
char* THE_NAME = NULL;

/*
 * ------------------------------------------------- functions --
 */
/**
 *
 * \brief get shared memory
 *
 * Creates or returns a shared memory of given size with given key
 *
 * \param memkey key for shared memory
 * \param size size of shared memory
 *
 * \return id to access the shared memory
 *
 */
int get_shmem(key_t memkey, size_t size)
{

    if ((SHARED_MEMORY_ID = shmget(memkey,size,PERMISSIONS|IPC_CREAT)) == -1)
    {
        fprintf(stderr,"Memory Allocation failed: %s", strerror(errno));
    }

    return SHARED_MEMORY_ID;
}

/**
 *
 * \brief creates semaphores
 *
 * Returns and in case that there is no sempahre for given key creates it.
 *
 * \param semkey key of the semaphore
 * \param initval initial value of the semaphore
 *
 * \return semaphore id for using it
 *
 */
int get_semid(key_t semkey, int initval)
{
    int sem = -1;
    if ( (sem = semgrab(semkey)) == -1 )
    {
        if (( sem = seminit(semkey,PERMISSIONS,initval)) == -1 )
        {
            fprintf(stderr,"Semaphore Creation failed: %s", strerror(errno));
        }
    }
    return sem;
}

/**
 *
 * \brief attach a shared memory to a process
 *
 * attaches a shared memory to a process, so that it can access it.
 * also sets the internal locks, so we know, which process still
 * has an attached memory.
 *
 * \param shmid id of memory to attach
 * \param pnShm pointer to start of memory
 * \param process id of process
 *
 */
void attach_shmem(int shmid, int **pnShm, int process)
{
    if ( process == SEMKEY_R )
    {
        SHM_READ = 1;
    }
    else if ( process == SEMKEY_W )
    {
        SHM_WRITE = 1;
    }
    else
    {
        fprintf(stderr,"Wrong Process Specified");
        *pnShm = (int *) -1;
    }
    if ( (*pnShm = shmat(shmid,NULL,0)) == (int * ) -1)
    {
        fprintf(stderr,"Shared Memory attaching failed: %s", strerror(errno));
    }
}

/**
 *
 * \brief detach a shared memory from a process
 *
 * detaches a shared memory from process, so that it can be deleted.
 * also removes the internal locks, so we know, which process still
 * has an attached memory.
 *
 * \param key of shared memory
 * \param process id of process
 *
 */
int detach_shmem(int key,int process){
  int *pnShm = shmat(key,NULL,0);
  errno = 0;
  process = process;
  if(*pnShm == -1 || errno == EIDRM)
  {
    /* dann wurde der memory schon detached oder ist nicht vorhanden. */
   return 0;
  }

  if(shmdt(pnShm) == -1)
    {
        fprintf(stderr,"Shared Memory detach failed :%s", strerror(errno));
        return -1;
    }
  /* *pnShm = NULL; */
  return 0;
}

/**
 *
 * \brief removes all resources
 *
 * tries to remove semaphore, detach shared memory, and if last
 * process clears its lock, tries to remove the shared memory.
 * will try to remove everything. if nothing bad happens, it
 * will run to the end.
 *
 * \param semid id of semaphore to release
 * \param shmaddr start address of the shared memory
 * \param shmid shared memory id
 * \param process key of process
 *
 */
int clean_up(int semid, int **shmaddr, int shmid, int process)
{
semid = semid;
if(process == ERROR || process == SEMKEY_R){
  if( (semgrab(SEMKEY_R)) != -1) {
    if ( semrm(semgrab(SEMKEY_R)) == -1 )
      {
        fprintf(stderr,"semaphore deletion failed: %s", strerror(errno));
      }
  }
    if(semgrab(SEMKEY_W) != -1) {
    if ( (semrm(semgrab(SEMKEY_W))) == -1 )
      {
        fprintf(stderr,"semaphore deletion failed: %s", strerror(errno));
      }
  }
}
  (void) detach_shmem(shmid,process);
  *shmaddr = NULL;

        if ( shmctl( shmid, IPC_RMID, NULL ) == -1 )
        {
            fprintf(stderr,"Shared Memory Deletion failed: %s", strerror(errno));
        }
    return 0;
}


/**
 *
 * \brief checks the params
 *
 * checks if params are valid, in the form of: -m \<ringbuffer elements\>
 * and if there are more params than specified
 * also checks if \<ringbuffer elements\> is a valid number and in the range of INT_MAX
 * \param argc from main()
 * \param *argv[] from main()
 * \return returns the size for the ringbuffer or -1 if ERROR
 *
 */
extern int check_arguments(int argc, char *argv[])
{
    long int size = -1;
    int opt = -1;
    char *endptr = NULL;

    errno = 0;
    THE_NAME = argv[0];


    while((opt = getopt(argc, argv, "-m:")) != -1)
    {

      switch(opt)
	{
	case 'm':
	   size = strtol(optarg, &endptr, 10);
	   /*ERRORs checken */
	   if ((errno == ERANGE && (size == LONG_MAX || size == LONG_MIN))
                   || (errno != 0 && size == 0)) {
                return ERROR;
           }

           if (optarg == '\0' || *endptr != '\0')
           {
                return ERROR;
           }
           if(size > INT_MAX)
            {
                return ERROR;
            }
	   break;
	case '?':
	case 'h':
	  print_usage();
	  break;
	default:
	  return ERROR;
	}
      
    }

    if (optind > argc)
    {
        return ERROR;
    }

    return size;

}

/**
 *
 * \brief prints the usage
 *
 */
void print_usage(void)
{
    fprintf(stderr, "%s Usage: -m <ringbuffer elements> \n", THE_NAME);

}
